package com.wvmi.gmall.product.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wvmi.gmall.product.service.CategoryBrandRelationService;
import com.wvmi.gmall.product.vo.Catelog2Vo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wvmi.gmall.common.utils.PageUtils;
import com.wvmi.gmall.common.utils.Query;

import com.wvmi.gmall.product.dao.CategoryDao;
import com.wvmi.gmall.product.entity.CategoryEntity;
import com.wvmi.gmall.product.service.CategoryService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    CategoryBrandRelationService categoryBrandRelationService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> queryListWithTree() {
        List<CategoryEntity> entities = baseMapper.selectList(null);
        return entities.stream().filter(e -> e.getParentCid() == 0)
                .peek(e -> e.setChildren(getChildren(e, entities)))
                .sorted(Comparator.comparingInt(e -> (e.getSort() == null ? 0 : e.getSort())))
                .collect(Collectors.toList());
    }

    private List<CategoryEntity> getChildren(CategoryEntity root, List<CategoryEntity> all) {
        return all.stream().filter(e-> e.getParentCid().equals(root.getCatId()))
                .peek(e->e.setChildren(getChildren(e,all)))
                .sorted(Comparator.comparingInt(e -> (e.getSort() == null ? 0 : e.getSort())))
                .collect(Collectors.toList());
    }

    @Override
    public void deleteMenuByIds(List<Long> asList) {
        baseMapper.deleteBatchIds(asList);
    }

    @Override
    public Long[] findCatelogPath(Long catelogId) {
        List<Long> paths = new ArrayList<>();
        List<Long> parentPath = findParentPath(catelogId, paths);

        Collections.reverse(parentPath);


        return parentPath.toArray(new Long[parentPath.size()]);
    }
    //225,25,2
    private List<Long> findParentPath(Long catelogId,List<Long> paths){
        //1、收集当前节点id
        paths.add(catelogId);
        CategoryEntity byId = this.getById(catelogId);
        if(byId.getParentCid()!=0){
            findParentPath(byId.getParentCid(),paths);
        }
        return paths;

    }
    @Transactional
    @Override
    @CacheEvict(value = "category",key = "'getLevel1Categorys'")
    public void updateCascade(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
    }

    @Cacheable(value = "category",key = "#root.methodName")
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        long l = System.currentTimeMillis();
        List<CategoryEntity> categoryEntities = this.baseMapper.selectList(
                new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        System.out.println("消耗时间："+ (System.currentTimeMillis() - l));
        return categoryEntities;
    }

    @Override
    public Map<String, List<Catelog2Vo>> getCatalogJson() {
        try {
            String catalogJson = stringRedisTemplate.opsForValue().get("catalogJson");
            if(StringUtils.isEmpty(catalogJson)){
                return getCatalogJsonDb();
            }
            System.out.println("查缓存111。。。。。");
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.readValue(catalogJson, new TypeReference<Map<String, List<Catelog2Vo>>>(){});
        } catch (IOException e) {
            log.error("从缓存中获取数据有问题",e);
            return getCatalogJsonDb();
        }
    }

    public Map<String, List<Catelog2Vo>> getCatalogJsonWithRedisson() {
        RLock lock = redissonClient.getLock("catalogJson_lock");
        try {
            // 加锁保证原子性
            lock.lock();
            System.out.println("等待查数据库。。");
            String catalogJson = stringRedisTemplate.opsForValue().get("catalogJson");
            ObjectMapper objectMapper = new ObjectMapper();
            if(StringUtils.isEmpty(catalogJson)){
                System.out.println("查数据库了。。。。。");
                //将数据库的多次查询变为一次
                List<CategoryEntity> selectList = this.baseMapper.selectList(null);

                //1、查出所有分类
                //1、1）查出所有一级分类
                List<CategoryEntity> level1Categorys = getParent_cid(selectList, 0L);

                //封装数据
                Map<String, List<Catelog2Vo>> parentCid = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
                    //1、每一个的一级分类,查到这个一级分类的二级分类
                    List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());

                    //2、封装上面的结果
                    List<Catelog2Vo> catelog2Vos = null;
                    if (categoryEntities != null) {
                        catelog2Vos = categoryEntities.stream().map(l2 -> {
                            Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, l2.getCatId().toString(), l2.getName().toString());

                            //1、找当前二级分类的三级分类封装成vo
                            List<CategoryEntity> level3Catelog = getParent_cid(selectList, l2.getCatId());

                            if (level3Catelog != null) {
                                List<Catelog2Vo.Category3Vo> category3Vos = level3Catelog.stream().map(l3 -> {
                                    //2、封装成指定格式
                                    return new Catelog2Vo.Category3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());
                                }).collect(Collectors.toList());
                                catelog2Vo.setCatalog3List(category3Vos);
                            }

                            return catelog2Vo;
                        }).collect(Collectors.toList());
                    }
                    return catelog2Vos;
                }));

                String s = objectMapper.writeValueAsString(parentCid);
                stringRedisTemplate.opsForValue().set("catalogJson",s);
                return parentCid;
            }
            System.out.println("查缓存了。。");
            return objectMapper.readValue(catalogJson, new TypeReference<Map<String, List<Catelog2Vo>>>(){});

        } catch (IOException e){
            return getCatalogJsonDb();
        } finally {
            lock.unlock();
        }
    }


    public Map<String, List<Catelog2Vo>> getCatalogJsonDb() {
        try {
            // 加锁保证原子性
            String uuid = UUID.randomUUID().toString();
            Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,300, TimeUnit.SECONDS);
            if(Boolean.TRUE.equals(lock)){
                System.out.println("等待查数据库。。");
                String catalogJson = stringRedisTemplate.opsForValue().get("catalogJson");
                ObjectMapper objectMapper = new ObjectMapper();
                if(StringUtils.isEmpty(catalogJson)){
                    System.out.println("查数据库了。。。。。");
                    //将数据库的多次查询变为一次
                    List<CategoryEntity> selectList = this.baseMapper.selectList(null);

                    //1、查出所有分类
                    //1、1）查出所有一级分类
                    List<CategoryEntity> level1Categorys = getParent_cid(selectList, 0L);

                    //封装数据
                    Map<String, List<Catelog2Vo>> parentCid = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
                        //1、每一个的一级分类,查到这个一级分类的二级分类
                        List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());

                        //2、封装上面的结果
                        List<Catelog2Vo> catelog2Vos = null;
                        if (categoryEntities != null) {
                            catelog2Vos = categoryEntities.stream().map(l2 -> {
                                Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, l2.getCatId().toString(), l2.getName().toString());

                                //1、找当前二级分类的三级分类封装成vo
                                List<CategoryEntity> level3Catelog = getParent_cid(selectList, l2.getCatId());

                                if (level3Catelog != null) {
                                    List<Catelog2Vo.Category3Vo> category3Vos = level3Catelog.stream().map(l3 -> {
                                        //2、封装成指定格式
                                        return new Catelog2Vo.Category3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());
                                    }).collect(Collectors.toList());
                                    catelog2Vo.setCatalog3List(category3Vos);
                                }

                                return catelog2Vo;
                            }).collect(Collectors.toList());
                        }
                        return catelog2Vos;
                    }));

                    String s = objectMapper.writeValueAsString(parentCid);
                    stringRedisTemplate.opsForValue().set("catalogJson",s);
                    String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";
                    Long delRes = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList("lock"), uuid);
                    return parentCid;
                }
                System.out.println("查缓存了。。");
                return objectMapper.readValue(catalogJson, new TypeReference<Map<String, List<Catelog2Vo>>>(){});
            } else{
                try {
                    Thread.sleep(200);
                    return getCatalogJsonDb();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        } catch (IOException e){
            return getCatalogJsonDb();
        }
    }

    private List<CategoryEntity> getParent_cid(List<CategoryEntity> selectList,Long parentCid) {
        return selectList.stream().filter(item -> item.getParentCid().equals(parentCid)).collect(Collectors.toList());
    }
}